package cn.ibizlab.core.oauth2.service.impl;

import cn.ibizlab.core.authentication.domain.AuthGrantedRole;
import cn.ibizlab.core.authentication.domain.AuthGrantedSystem;
import cn.ibizlab.core.authentication.domain.AuthUserImpl;
import cn.ibizlab.core.authentication.mapping.AuthGrantedSystemMapping;
import cn.ibizlab.core.authentication.service.impl.AuthUserServiceImpl;
import cn.ibizlab.core.authorization.domain.SysGrantedAuthority;
import cn.ibizlab.core.authorization.domain.SysRole;
import cn.ibizlab.core.authorization.service.SysGrantedAuthorityService;
import cn.ibizlab.core.authorization.service.SysRoleService;
import cn.ibizlab.core.system.domain.SysDeploySystem;
import cn.ibizlab.core.system.service.SysDeploySystemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.stream.Collectors;

import static cn.ibizlab.core.oauth2.constants.OAuth2Constant.PREDEFINE_CLIENTDETAILS_ID;

@Primary
@Service("OAuth2AuthorizeService")
public class OAuth2AuthorizeService extends AuthUserServiceImpl {

    @Autowired
    SysRoleService sysRoleService;
    @Autowired
    SysGrantedAuthorityService sysGrantedAuthorityService;
    @Autowired
    AuthGrantedSystemMapping authGrantedSystemMapping;
    @Autowired
    SysDeploySystemService sysDeploySystemService;

    private static final String GLOBAL_ROLE = "_global";

    private static final String ROLE_SUPER_ADMIN = "ROLE_SUPERADMIN";

    private static final String ROLE_API_USERS = "ROLE_APIUSERS";

    @Override
    public AuthUserImpl loadUserByUsername(String username) {
        //是否为授权应用登录
        if (!isAuthorizeAppLogin())
            return super.loadUserByUsername(username);

        //根据登录用户创建授权用户
        return createAuthUser(SecurityContextHolder.getContext().getAuthentication());
    }

    /**
     * 创建授权应用认证用户
     * 1.查询登录用户的授权配置
     * 2.根据授权配置查询关联的角色及资源
     * 3.根据角色及资源创建认证用户
     * @param authentication
     * @return
     */
    protected AuthUserImpl createAuthUser(Authentication authentication) {
        boolean superUser = false;
        Set<String> grantedRoleAuthorityList = new LinkedHashSet<>();
        //查询授权用户权限资源
        if (!ObjectUtils.isEmpty(authentication.getAuthorities())) {
            for (GrantedAuthority authority : authentication.getAuthorities()) {
                grantedRoleAuthorityList.add(authority.getAuthority());
                if (ROLE_SUPER_ADMIN.equals(authority.getAuthority())||ROLE_API_USERS.equals(authority.getAuthority()))
                    superUser = true;
            }
        }


        //查询授权用户权限资源关联的角色
        Map<String,SysRole> grantedRoleMaps = new LinkedHashMap<>();
        List<String> grantedRoleIds = new ArrayList<>();
        if (!ObjectUtils.isEmpty(grantedRoleAuthorityList)) {
            List<SysRole> sysRoles = sysRoleService.all(new SysRole()).stream().filter(e -> grantedRoleAuthorityList.contains(e.getAuthority())||grantedRoleAuthorityList.contains(e.getId())).collect(Collectors.toList());
            if (!ObjectUtils.isEmpty(sysRoles)) {
                sysRoles.forEach(sysRole -> {
                    grantedRoleMaps.put(sysRole.getId(), sysRole);
                    grantedRoleMaps.put(sysRole.getAuthority(), sysRole);
                    grantedRoleIds.add(sysRole.getId());
                });
            }
        }


        //查询授权用户权限资源关联的角色资源
        Map<String, List<SysGrantedAuthority>> roleAuthorityMaps = null;
        if (!ObjectUtils.isEmpty(grantedRoleIds))
            roleAuthorityMaps = sysGrantedAuthorityService.findByRoleId(grantedRoleIds).stream().collect(Collectors.groupingBy(e -> e.getRoleId()));

        //构造认证用户
        Map<String,List<AuthGrantedRole>> grantedRoles = new LinkedHashMap<>();
        for (String grantedRoleAuthority : grantedRoleAuthorityList) {
            SysRole sysRole = grantedRoleMaps.get(grantedRoleAuthority);
            String deploySystemId = GLOBAL_ROLE;
            AuthGrantedRole grantedRole = null;
            if(sysRole == null)
                grantedRole = new AuthGrantedRole().setRoleId(grantedRoleAuthority).setDisplayName(grantedRoleAuthority).setAuthority(grantedRoleAuthority);
            else {
                grantedRole = authGrantedSystemMapping.toAuthGrantedRole(sysRole);
                if (!ObjectUtils.isEmpty(sysRole.getDeploySystemId()))
                    deploySystemId = sysRole.getDeploySystemId();
            }
            if (!ObjectUtils.isEmpty(roleAuthorityMaps) && roleAuthorityMaps.containsKey(grantedRoleAuthority) && !superUser)
                grantedRole.setAuthorities(authGrantedSystemMapping.toAuthGranted(roleAuthorityMaps.get(grantedRoleAuthority)));
            if (!grantedRoles.containsKey(deploySystemId))
                grantedRoles.put(deploySystemId,new ArrayList<>());
            grantedRoles.get(deploySystemId).add(grantedRole);
        }
        List<AuthGrantedSystem> authGrantedSystems = new ArrayList<>();

        List<SysDeploySystem> allSystems = new ArrayList<>(sysDeploySystemService.all(new SysDeploySystem()));
        allSystems.add(new SysDeploySystem().setId(GLOBAL_ROLE).setSystemId(GLOBAL_ROLE).setDisplayName("全局"));
        allSystems.stream().filter(e->grantedRoles.containsKey(e.getId())).collect(Collectors.toList()).forEach(sysDeploySystem -> {
            AuthGrantedSystem authGrantedSystem = authGrantedSystemMapping.toAuthGrantedSystem(sysDeploySystem);
            authGrantedSystem.setRoles(grantedRoles.get(sysDeploySystem.getId()));
            authGrantedSystems.add(authGrantedSystem);
        });
        AuthUserImpl authUser = new AuthUserImpl();
        authUser.setId(authentication.getName());
        authUser.setUsername(authentication.getName());
        authUser.setDisplayName(authentication.getName());
        authUser.setRoles(authGrantedSystems);
        return authUser;
    }

    /**
     * 当前是否为授权应用登录
     *
     * @return
     */
    protected boolean isAuthorizeAppLogin() {
        if (SecurityContextHolder.getContext() != null && SecurityContextHolder.getContext().getAuthentication() != null
                && !(SecurityContextHolder.getContext().getAuthentication() instanceof AnonymousAuthenticationToken)
                && !PREDEFINE_CLIENTDETAILS_ID.equals(SecurityContextHolder.getContext().getAuthentication().getPrincipal())) {
            return true;
        }
        return false;
    }
}
